#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h> // 信号量
#include <sys/ipc.h> // IPC
#include <sys/shm.h> // 共享内存
#include <fcntl.h>
typedef union semun
{
  int val;
  struct semid_ds *buf;
  unsigned short *array;
} semun;

void init_ops(struct sembuf *ops)
{
  ops[0].sem_num = 0;
  ops[0].sem_op = -1;
  ops[0].sem_flg = SEM_UNDO;

  ops[1].sem_num = 1;
  ops[1].sem_op = -1;
  ops[1].sem_flg = SEM_UNDO;

  ops[2].sem_num = 2;
  ops[2].sem_op = -1;
  ops[2].sem_flg = SEM_UNDO;

  ops[3].sem_num = 0;
  ops[3].sem_op = 1;
  ops[3].sem_flg = SEM_UNDO;

  ops[4].sem_num = 1;
  ops[4].sem_op = 1;
  ops[4].sem_flg = SEM_UNDO;

  ops[5].sem_num = 2;
  ops[5].sem_op = 1;
  ops[5].sem_flg = SEM_UNDO;
}

int main(int argc, char *argv[])
{

  key_t key_1, key_2;
  int shmid, semid, fd;
  char *ptr, *temp, filename[1024], buf[1024];
  int count = 0;

  // 获取key
  key_1 = ftok("/", 'a');
  key_2 = ftok("/", 'b');
  if (key_1 < 0 || key_2 < 0)
  {
    perror("获取key失败");
    exit(1);
  }
  // 获取共享内存id
  shmid = shmget(key_1, 1024, 0666 | IPC_CREAT);
  if (shmid < 0)
  {
    perror("获取共享内存失败");
    exit(1);
  }
  // 获取信号量id
  semid = semget(key_2, 3, 0666 | IPC_CREAT);
  if (semid < 0)
  {
    perror("获取信号量失败");
    exit(1);
  }

  // 初始化信号量
  semun arg;
  unsigned short arr[] = {1, 0, 1}; // 信号量值
  arg.array = arr;
  semctl(semid, 0, SETALL, arg); // 设置信号量

  // 初始化信号量操作
  struct sembuf ops[6];
  init_ops(ops);

  // 获取共享内存地址
  ptr = shmat(shmid, NULL, 0);
  if (ptr == (char *)-1)
  {
    perror("获取共享内存地址失败");
    exit(1);
  }

  // 接受用户输入的文件名

  // write(1, "请输入文件名：\n", 23);
  // count = read(0, filename, 1024);
  // if (count <= 0)
  // {
  //   perror("输入文件名失败");
  //   exit(1);
  // }
  if (argc != 2)
  {
    printf("Usage: ./send filename\n");
    exit(1);
  }
  // strcpy(filename, argv[1]);
  // filename[count - 1] = '\0'; // 去除换行符

  // 打开文件
  fd = open(argv[1], O_RDONLY);
  if (fd < 0)
  {
    perror("打开文件失败");
    exit(1);
  }
  while (1) // 循环向共享内存写数据
  {

    // 执行操作
    // 从文件读数据
    count = read(fd, buf, sizeof(buf));
    if (count > 0)
    {
      // 等待空间
      semop(semid, &ops[2], 1);
      // 等待锁
      semop(semid, &ops[0], 1);
      // 写入共享内存
      strncpy(ptr, buf, count);
      // printf("发送数据：%s\n", buf);
      // 清空buf
      memset(buf, 0, sizeof(buf));
      // 释放锁
      semop(semid, &ops[3], 1);
      // 产品数+1
      semop(semid, &ops[4], 1);
    }
    else if (count == 0)
    {
      // 等待空间
      semop(semid, &ops[2], 1);
      // 等待锁
      semop(semid, &ops[0], 1);
      // 写入共享内存
      strncpy(ptr, "end\0", 5);
      // 清空buf
      memset(buf, 0, sizeof(buf));
      // 释放锁
      semop(semid, &ops[3], 1);
      // 产品数+1
      semop(semid, &ops[4], 1);

      break;
    }
  }
  close(fd);
  shmdt(ptr);
  printf("文件发送完毕\n");
  return 0;
}